﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web;
using System.Xml;

namespace Vbyte.DataSource.Utility
{
    /// <summary>
    /// XML数据解析
    /// </summary>
    public class XMLParse
    {
        /// <summary>
        /// 初始化一个XML解析器
        /// </summary>
        /// <param name="xml">xml文档内容</param>
        public XMLParse(string xml)
        {
            xmlContent = xml;
        }


        private string xmlContent = null;

        /// <summary>
        /// 输出C#实例类
        /// </summary>
        /// <param name="writer">文本输出器</param>
        public void ToEntryClass(TextWriter writer)
        {
            XmlDocument xDoc = new XmlDocument();
            xDoc.LoadXml(xmlContent);

            //XmlTextWriter xWriter = new XmlTextWriter(writer);
            //xWriter.Formatting = Formatting.Indented;
            //xDoc.WriteContentTo(xWriter);

            //数组对象构建词典
            Dictionary<string, bool> entryBuildDict = new Dictionary<string, bool>();

            //内嵌对象构建
            Dictionary<string, XmlNode> subNodeDict = new Dictionary<string, XmlNode>();

            writer.WriteLine("public class " + xDoc.DocumentElement.Name);
            writer.WriteLine("{");
            WriteNodeList(xDoc.DocumentElement, writer, entryBuildDict, subNodeDict, 1);
            writer.WriteLine("}");
            //writer.WriteLine(xDoc.OuterXml);

            foreach (string key in subNodeDict.Keys)
            {
                //writer.WriteLine(subNodeDict[key].Name);
               // WriteNodeList(xDoc.DocumentElement, writer, entryBuildDict, subNodeDict);
                writer.WriteLine();
                WriteNodeEntry(writer, subNodeDict[key]);
            }

        }

        private void WriteNodeEntry(TextWriter writer, XmlNode node)
        {
            writer.WriteLine("[Serializable]");
            //writer.WriteLine("public class " + JSONParse.CapitalUpperCase(node.Name));
            writer.WriteLine("public class " + node.Name);
            writer.WriteLine("{");
            //WriteSingleNode(node, writer, false, null, null, -1);
            WriteNodeList(node, writer, null, null, -1);
            writer.WriteLine("}");
        }

        private void WriteProperty(TextWriter writer, bool isArray, bool isObject, string propName, object propValue)
        {
            if (isArray)
            {
                writer.WriteLine(new string('\t', 1) + "private {0}[] _{0} = new {0}[0];", propName);
                writer.WriteLine(new string('\t', 1) + "public {0}[] {1}", propName, propName);
                writer.WriteLine(new string('\t', 1) + "{");
                writer.WriteLine(new string('\t', 2) + "get { return _" + propName + ";}");
                writer.WriteLine(new string('\t', 2) + "set { _" + propName + " = value; }");
                writer.WriteLine(new string('\t', 1) + "}");
            }
            else
            {
                writer.WriteLine(new string('\t', 1) + "public {3} {0} {1} get; set; {2}", propName,
                        "{", "}",
                        (isObject) ? propName : JSONParse.GetDataTypeByExample(propValue));
            }
        }

        private bool IsContainSubObject(XmlNode node)
        {
            return (node.HasChildNodes &&
                    (node.ChildNodes.Count > 1 ||
                    (node.ChildNodes.Count == 1 && node.ChildNodes[0].NodeType == XmlNodeType.Element)
                    ));
        }


        private void WriteSingleNode(XmlNode node, TextWriter writer, bool isArray,
            Dictionary<string, bool> entryBuildDict,
            Dictionary<string, XmlNode> subNodeDict, int deepth)
        {
            //Add Property
            if (!node.HasChildNodes)
            {
                WriteProperty(writer, isArray, false, node.Name, "");
            }
            else
            {
                if (IsContainSubObject(node) == false)
                {
                    WriteProperty(writer, isArray, false, node.Name, node.InnerText.Trim());
                }
                else
                {
                        //writer.WriteLine("{0}:{1}内嵌对象：", node.Name, deepth);

                        //if (subNodeDict != null && subNodeDict.Count >= 0)
                        //{
                            WriteProperty(writer, isArray, true, node.Name, "");
                        //}

                        //内嵌对象
                        WriteSubObject(node, writer, entryBuildDict, subNodeDict, deepth);

                }
            }
        }


        private void WriteNodeList(XmlNode parentNode, TextWriter writer,
            Dictionary<string, bool> entryBuildDict,
            Dictionary<string, XmlNode> subNodeDict, int deepth)
        {
            XmlNodeList xNodeList = parentNode.ChildNodes;
            string lastNodeName = string.Empty;
            XmlNode node = null, preNode = null;
            for (int i = 0; i < xNodeList.Count; i++)
            {
                node = xNodeList[i];
                if (node.NodeType != XmlNodeType.Element) continue;

                //writer.WriteLine("current: " + node.Name);
                if (lastNodeName != node.Name)
                {
                    //第1个节点
                    if (lastNodeName == string.Empty && node.NextSibling != null && node.NextSibling.Name != node.Name)
                    {
                        WriteSingleNode(node, writer, false, entryBuildDict, subNodeDict, deepth);
                    }
                    else
                    {
                        if (node.PreviousSibling != null && node.PreviousSibling.Name != node.Name)
                        {
                            WriteSingleNode(node, writer, false, entryBuildDict, subNodeDict, deepth);
                        }
                    }

                    preNode = node;
                    lastNodeName = node.Name;

                }
                else
                {
                    //同名节点生成数组
                    string nodeKey = string.Format("{0}:{1}", node.Name, deepth);
                    if (!subNodeDict.ContainsKey(nodeKey))
                    {
                        WriteSingleNode(node, writer, true, entryBuildDict, subNodeDict, deepth);
                        subNodeDict.Add(nodeKey, node);
                        //entryBuildDict.Add(node.Name, false);
                    }
                    continue;
                }
            }
        }

        private void WriteSubObject(XmlNode node, TextWriter writer,
            Dictionary<string, bool> entryBuildDict,
            Dictionary<string, XmlNode> subNodeDict, int deepth)
        {
            string nodeKey = string.Format("{0}:{1}", node.Name, deepth);
            //writer.WriteLine("Debug:" + node.Name);

            if (subNodeDict != null)
            {
                if (!subNodeDict.ContainsKey(nodeKey))
                {
                    subNodeDict.Add(nodeKey, node);
                }
                else
                {
                    return;
                }
            }

            //writer.WriteLine();
            //writer.WriteLine("Name:{0}, Deepth:{1} ", node.Name, deepth);
            //writer.WriteLine();

            XmlNode currentNode = null;
            for (int i = 0; i < node.ChildNodes.Count; i++)
            {
                currentNode = node.ChildNodes[i];

                //判定是否有内嵌对象
                if (IsContainSubObject(currentNode))
                {
                    //writer.WriteLine("{0}:{1}内嵌对象，下级数量：({2})", currentNode.Name, deepth + 1, currentNode.ChildNodes.Count);
                    //内嵌对象
                    WriteSubObject(currentNode, writer, entryBuildDict, subNodeDict, deepth + 1);
                }

            }


        }

        /// <summary>
        /// URL地址编码形式
        /// </summary>
        /// <returns></returns>
        public string ToUrlString()
        {
            return HttpUtility.UrlEncode(xmlContent);
        }

    }
}
